0%

SpringAI — RAG(检索增强生成)

概述

检索增强生成(RAG: Retrieval Augmented Generation)是一种用于克服大型语言模型在长文本内容、事实准确性和上下文感知方面局限性的技术。

Spring AI 通过提供模块化架构来支持 RAG,允许你自行构建自定义 RAG 流程,或使用 Advisor API 提供的开箱即用 RAG 流程。

使用 QuestionAnswerAdvisor

依赖配置

要使用 QuestionAnswerAdvisorVectorStoreChatMemoryAdvisor,需要在项目中添加 spring-ai-vector-store-advisor 依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vector-store-advisor</artifactId>
<version>${spring-ai.version}</version>
</dependency>

基本用法

向量数据库存储了 AI 模型未知的数据,当用户问题发送给 AI 模型时,QuestionAnswerAdvisor 会查询向量数据库中与用户问题相关的文档。向量数据库的响应会附加到用户文本后面,为 AI 模型生成响应提供上下文。

假设你已经将数据加载到 VectorStore 中,你可以通过向 ChatClient 提供 QuestionAnswerAdvisor 实例来执行检索增强生成(RAG):

1
2
3
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
.build();

搜索过滤

QuestionAnswerAdvisor 会将对向量数据库中的所有文档执行相似性搜索。为了限制搜索的文档类型,SearchRequest 接受一个类似 SQL 的过滤表达式,该表达式可在所有 VectorStore 之间移植。

这个过滤表达式可以在创建 QuestionAnswerAdvisor 时配置,因此将始终应用于所有 ChatClient 请求,也可以在运行时按请求提供。

以下是创建 QuestionAnswerAdvisor 的示例,其中阈值(threshold)为 0.8,返回前 6 个结果:

1
2
3
4
5
QuestionAnswerAdvisor advisor = new QuestionAnswerAdvisor(vectorStore,
SearchRequest.builder()
.similarityThreshold(0.8)
.topK(6)
.build());

运行时动态过滤

使用 FILTER_EXPRESSION advisor 上下文参数在运行时更新 SearchRequest 过滤表达式:

1
2
3
4
5
6
chatClient.prompt()
.user("请介绍一下 Spring AI")
.advisors(advisor -> advisor
.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "metadata.key = 'someValue'"))
.call()
.content();

FILTER_EXPRESSION 参数允许你根据提供的表达式动态过滤搜索结果。

自定义提示词模板

QuestionAnswerAdvisor 使用默认模板将检索到的文档与用户问题结合起来。你可以通过 .promptTemplate() 构建器方法提供自己的 PromptTemplate 对象来自定义此行为:

1
2
3
4
5
QuestionAnswerAdvisor advisor = new QuestionAnswerAdvisor(vectorStore,
SearchRequest.builder().build(),
PromptTemplate.builder()
.template("Context: {question_answer_context}\n\nQuestion: {query}\n\nAnswer:")
.build());

自定义 PromptTemplate 可以使用任何 TemplateRenderer 实现(默认使用基于 StringTemplate 引擎的 StPromptTemplate)。重要的是模板必须包含以下两个占位符:

  • query — 接收用户问题
  • question_answer_context — 接收检索到的上下文

使用 RetrievalAugmentationAdvisor

Spring AI 包含一个 RAG 模块库,你可以使用它来构建自己的 RAG 流程。RetrievalAugmentationAdvisor 是一个基于模块化架构为最常见 RAG 流程提供开箱即用实现的 Advisor。

依赖配置

要使用 RetrievalAugmentationAdvisor,你需要在项目中添加 spring-ai-rag 依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-rag</artifactId>
<version>${spring-ai.version}</version>
</dependency>

基本用法

1
2
3
4
5
6
7
8
9
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.build();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(advisor)
.build();

默认情况下,RetrievalAugmentationAdvisor 不允许检索到的上下文为空。当发生这种情况时,它会指示模型不要回答用户查询。你可以按如下方式允许空上下文:

1
2
3
4
5
6
7
8
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(true)
.build())
.build();

过滤表达式

VectorStoreDocumentRetriever 接受 FilterExpression 以根据元数据过滤搜索结果。你可以在实例化 VectorStoreDocumentRetriever 时提供一个,也可以在运行时按请求使用 FILTER_EXPRESSION advisor 上下文参数提供:

1
2
3
4
5
6
7
chatClient.prompt()
.user("请介绍一下 Spring AI")
.advisors(advisor -> advisor
.param(RetrievalAugmentationAdvisor.FILTER_EXPRESSION,
"metadata.category = 'technology'"))
.call()
.content();

文档后处理

你还可以使用 DocumentPostProcessor API 在将检索到的文档传递给模型之前对其进行后处理。例如,你可以使用此类接口根据文档与查询的相关性执行重新排序、删除不相关或冗余的文档,或压缩每个文档的内容以减少噪声和冗余。


模块化 RAG 架构

Spring AI 实现了模块化 RAG 架构,灵感来自论文 “Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks” 中详细阐述的模块化概念。

架构概览

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────────────────┐
│ Pre-Retrieval │
│ 查询转换 / 查询扩展(Compression / Rewrite / Translation / Multi-Query) │
├─────────────────────────────────────────────────────────┤
│ Retrieval │
│ 文档检索(VectorStoreDocumentRetriever) / 文档合并(DocumentJoiner) │
├─────────────────────────────────────────────────────────┤
│ Post-Retrieval │
│ 文档后处理(DocumentPostProcessor) │
├─────────────────────────────────────────────────────────┤
│ Generation │
│ 查询增强 + 生成响应(ContextualQueryAugmenter) │
└─────────────────────────────────────────────────────────┘

Pre-Retrieval 模块(检索前)

Pre-Retrieval 模块负责处理用户查询,以获得最佳的检索结果。

查询转换(Query Transformation)

用于转换输入查询以使其更有效地执行检索任务的组件,解决格式不佳的查询、歧义术语、复杂词汇或不支持的语言等挑战。

使用 QueryTransformer 时,建议将 ChatClient.Builder 的温度配置得较低(例如 0.0),以确保结果更具确定性和准确性,从而提高检索质量。大多数聊天模型的默认温度通常过高,不利于查询转换,导致检索效率降低

CompressionQueryTransformer(压缩查询转换器)

CompressionQueryTransformer 使用大型语言模型将对话历史和后续查询压缩为一个独立的查询,捕捉对话的精髓。

当对话历史较长且后续查询与对话上下文相关时,此转换器非常有用。

该组件使用的提示词可以通过构建器中的 promptTemplate() 方法进行自定义。

RewriteQueryTransformer(重写查询转换器)

RewriteQueryTransformer 使用大型语言模型重写用户查询,以便在对目标系统(如向量存储或 Web 搜索引擎)进行查询时提供更好的结果。

当用户查询冗长、模糊或包含可能影响搜索结果质量的不相关信息时,此转换器非常有用。

该组件使用的提示词可以通过构建器中的 promptTemplate() 方法进行自定义。

TranslationQueryTransformer(翻译查询转换器)

TranslationQueryTransformer 使用大型语言模型将查询翻译为嵌入模型支持的目标语言(用于生成文档嵌入)。如果查询已经是目标语言,则原样返回。如果查询的语言未知,也将原样返回。

当嵌入模型在特定语言上训练而用户查询使用不同语言时,此转换器非常有用。

该组件使用的提示词可以通过构建器中的 promptTemplate() 方法进行自定义。

查询扩展(Query Expansion)

用于将输入查询扩展为查询列表的组件,通过提供替代查询表述或将复杂问题分解为更简单的子查询来解决格式不佳的查询等挑战。

MultiQueryExpander(多查询扩展器)

MultiQueryExpander 使用大型语言模型将查询扩展为多个语义多样的变体,以捕获不同的视角,有助于检索额外的上下文信息并增加找到相关结果的机会。

默认情况下,MultiQueryExpander 在扩展查询列表中包含原始查询。你可以通过构建器中的 includeOriginal 方法禁用此行为。

该组件使用的提示词可以通过构建器中的 promptTemplate() 方法进行自定义。

Retrieval 模块(检索)

Retrieval 模块负责查询向量存储等数据系统并检索最相关的文档。

DocumentRetriever(文档检索器)

负责从底层数据源(如搜索引擎、向量存储、数据库或知识图谱)检索 Document 的组件。

VectorStoreDocumentRetriever(向量存储文档检索器)

VectorStoreDocumentRetriever 从向量存储中检索与输入查询语义相似的文档。它支持基于元数据、相似度阈值和 top-k 结果的过滤。

过滤表达式可以是静态的或动态的。对于动态过滤表达式,你可以传递一个 Supplier

你也可以通过 Query API 使用 FILTER_EXPRESSION 参数提供请求特定的过滤表达式。如果同时提供了请求特定和检索器特定的过滤表达式,请求特定的过滤表达式优先

DocumentJoiner(文档合并器)

用于将基于多个查询和多个数据源检索到的文档合并为单个文档集合的组件。作为合并过程的一部分,它还可以处理重复文档和互逆排名策略。

ConcatenationDocumentJoiner(拼接文档合并器)

ConcatenationDocumentJoiner 通过拼接将基于多个查询和多个数据源检索到的文档合并为单个文档集合。如果存在重复文档,则保留第一次出现的文档。每个文档的分数保持原样。

Post-Retrieval 模块(检索后)

Post-Retrieval 模块负责处理检索到的文档,以获得最佳的生成结果。

DocumentPostProcessor(文档后处理器)

用于基于查询对检索到的文档进行后处理的组件,解决中间丢失(lost-in-the-middle)、模型上下文长度限制以及需要减少检索信息中的噪声和冗余等挑战。

例如,它可以根据文档与查询的相关性对文档进行排序、删除不相关或冗余的文档,或压缩每个文档的内容以减少噪声和冗余。

Generation 模块(生成)

Generation 模块负责基于用户查询和检索到的文档生成最终响应。

QueryAugmenter(查询增强器)

用于使用额外数据增强输入查询的组件,有助于为大型语言模型提供回答用户查询所需的上下文。

ContextualQueryAugmenter(上下文查询增强器)

ContextualQueryAugmenter 使用所提供文档内容的上下文数据来增强用户查询。

默认情况下,ContextualQueryAugmenter 不允许检索到的上下文为空。当发生这种情况时,它会指示模型不要回答用户查询。

你可以启用 allowEmptyContext 选项,允许模型在检索到的上下文为空时仍然生成响应:

1
2
3
ContextualQueryAugmenter augmenter = ContextualQueryAugmenter.builder()
.allowEmptyContext(true)
.build();

完整示例:构建自定义 RAG 流程

以下是一个使用模块化 RAG 组件构建完整 RAG 流程的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
// Pre-Retrieval: 压缩查询
.queryTransformer(CompressionQueryTransformer.builder()
.chatModel(chatModel)
.build())
// Retrieval: 从向量存储检索
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.7)
.topK(5)
.build())
// Post-Retrieval: 文档后处理
.documentPostProcessor((query, documents) -> {
// 自定义后处理逻辑
return documents;
})
// Generation: 上下文增强
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(false)
.build())
.build();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(advisor)
.build();

String response = chatClient.prompt()
.user("Spring AI 支持哪些 RAG 功能?")
.call()
.content();